package com.xuecheng.media.service.impl;

import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.vod.model.v20170321.CreateUploadVideoResponse;
import com.aliyuncs.vod.model.v20170321.GetPlayInfoResponse;
import com.aliyuncs.vod.model.v20170321.RefreshUploadVideoResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.api.media.model.aliyun.VodUploadRequest;
import com.xuecheng.api.media.model.aliyun.VodUploadToken;
import com.xuecheng.api.media.model.dto.MediaDTO;
import com.xuecheng.api.media.model.qo.QueryMediaModel;
import com.xuecheng.api.media.model.vo.ResourceUrlModel;
import com.xuecheng.common.domain.code.CommonErrorCode;
import com.xuecheng.common.domain.page.PageRequestParams;
import com.xuecheng.common.domain.page.PageVO;
import com.xuecheng.common.domain.response.RestResponse;
import com.xuecheng.common.enums.common.AuditEnum;
import com.xuecheng.common.enums.common.CommonEnum;
import com.xuecheng.common.enums.common.ResourceTypeEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.common.util.StringUtil;
import com.xuecheng.media.common.constant.MediaErrorCode;
import com.xuecheng.media.common.utils.AliyunVODUtil;
import com.xuecheng.media.convert.MediaConvert;
import com.xuecheng.media.entity.Media;
import com.xuecheng.media.mapper.MediaMapper;
import com.xuecheng.media.service.MediaService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.time.LocalDateTime;
import java.util.List;

/**
 * <p>
 * 媒资信息 服务实现类
 * </p>
 *
 * @author itcast
 */
@Slf4j
@Service
public class MediaServiceImpl extends ServiceImpl<MediaMapper, Media> implements MediaService {

    @Value("${aliyun.region}")
    private String region;
    @Value("${aliyun.accessKeyId}")
    private String accessKeyId;
    @Value("${aliyun.accessKeySecret}")
    private String accessKeySecret;


    /**
     * 获取上传凭证
     *
     * @param vodUploadRequest 媒体上传请求信息
     * @return 上传凭证
     */
    @Override
    public VodUploadToken createUploadToken(VodUploadRequest vodUploadRequest) {
        try {
            //判断关键数据
            String title = vodUploadRequest.getTitle();
            String fileName = vodUploadRequest.getFileName();
            if (StringUtil.isBlank(title) || StringUtil.isBlank(fileName)) {
                ExceptionCast.cast(CommonErrorCode.E_100101);
            }
            //创建客户端
            DefaultAcsClient client = AliyunVODUtil.initVodClient(region, accessKeyId, accessKeySecret);
            //获取响应
            CreateUploadVideoResponse response = AliyunVODUtil.createUploadVideo(client, title, fileName);
            //设置返回参数
            VodUploadToken vodUploadToken = new VodUploadToken();
            vodUploadToken.setUploadAuth(response.getUploadAuth());
            vodUploadToken.setVideoId(response.getVideoId());
            vodUploadToken.setRequestId(response.getRequestId());
            vodUploadToken.setUploadAddress(response.getUploadAddress());

            return vodUploadToken;
        } catch (Exception e) {
            log.error("获得文件上传凭证失败：{}", e.getMessage());
            ExceptionCast.castWithException(MediaErrorCode.E_140011, e);
            return null;
        }
    }

    /**
     * 刷新获取上传凭证
     *
     * @param videoId 视频id
     * @return 上传凭证
     */
    @Override
    public VodUploadToken refreshDUploadToken(String videoId) {
        try {
            //判断关键数据
            if (StringUtil.isBlank(videoId)) {
                ExceptionCast.cast(CommonErrorCode.E_100101);
            }
            //创建客户端
            DefaultAcsClient client = AliyunVODUtil.initVodClient(region, accessKeyId, accessKeySecret);
            RefreshUploadVideoResponse response = AliyunVODUtil.refreshUploadVideo(client, videoId);
            //设置返回参数
            VodUploadToken vodUploadToken = new VodUploadToken();
            vodUploadToken.setUploadAuth(response.getUploadAuth());
            vodUploadToken.setVideoId(videoId);
            vodUploadToken.setRequestId(response.getRequestId());
            vodUploadToken.setUploadAddress(response.getUploadAddress());

            return vodUploadToken;
        } catch (Exception e) {
            log.error("获得文件上传凭证失败：{}", e.getMessage());
            ExceptionCast.castWithException(MediaErrorCode.E_140011, e);
            return null;
        }
    }

    /**
     * 保存媒资信息
     *
     * @param dto 前端信息
     * @return 媒资信息
     * ///////////////////////////////////
     * 1.开启事务
     * 2.判断关键数据
     * 前端操作无法确定关键数据
     * 通过数据库的表确定关键数据==>(数据表结构不允许修改)
     * fileName.fileId,companyId
     * 媒资信息需要保存在机构下才有意义
     * 3.保存媒资信息
     * 根据文件类型做不同的处理
     * 1.视频:返回播放地址
     * 2.文档:返回预览地址
     */
    @Transactional
    public MediaDTO saveMedia(MediaDTO dto) {
        try {
            //1.判断关键数据
            if (StringUtil.isBlank(dto.getFilename()) ||
                    StringUtil.isBlank(dto.getFileId()) ||
                    ObjectUtils.isEmpty(dto.getCompanyId())
            ) {
                ExceptionCast.cast(CommonErrorCode.E_100101);
            }
            //2.保存媒资信息
            //2.1将dto转为po
            Media media = MediaConvert.INSTANCE.dto2entity(dto);

            //如果添加操作,设置id为空
            media.setId(null);
            //根据文件类型做不同的处理
            //如果是视频,拿到播放地址
            if (ResourceTypeEnum.VIDEO.getCode().equals(media.getType())) {
                DefaultAcsClient client = AliyunVODUtil.initVodClient(region, accessKeyId, accessKeySecret);
                GetPlayInfoResponse playInfo = AliyunVODUtil.getPlayInfo(client, media.getFileId());
                String playURL = playInfo.getPlayInfoList().get(0).getPlayURL();
                media.setUrl(playURL);
            }
            //设置审核状态
            media.setAuditStatus(AuditEnum.AUDIT_UNPAST_STATUS.getCode());

            boolean result = this.save(media);

            if (!result) {
                //保存媒资信息失败
                ExceptionCast.cast(MediaErrorCode.E_140001);
            }

            return MediaConvert.INSTANCE.entity2dto(media);

        } catch (Exception e) {
            ExceptionCast.cast(MediaErrorCode.E_140001);
            return null;
        }
    }

    /**
     * 分页查询媒资信息
     *
     * @param params 分页查询参数
     * @param model  查询条件
     * @return 媒资信息集合
     * <p>
     * 1.判断分页查询的条件(是否开启事务)
     * <p>
     * 2.判断条件查询的条件及关键数据
     * 3.查询数据并封装
     * 4.判断结果
     * 5.转换成pageVO对象并返回结果
     */
    @Override

    public PageVO<Media> queryMediaList(PageRequestParams params, QueryMediaModel model, Long companyId) {

        //判断分页查询的参数
        if (params.getPageNo() < 1) {
            params.setPageNo(PageRequestParams.DEFAULT_PAGE_NUM);
        }
        if (params.getPageSize() < 1) {
            params.setPageSize(PageRequestParams.DEFAULT_PAGE_SIZE);
        }
        Page<Media> page = new Page<>(params.getPageNo(), params.getPageSize());

        //创建查询参数
        LambdaQueryWrapper<Media> queryWrapper = new LambdaQueryWrapper<>();
        //状态要属于正常状态
        queryWrapper.eq(Media::getStatus, CommonEnum.USING_FLAG.getCode());
        if (!ObjectUtils.isEmpty(companyId)) {
            //媒资要属于一家机构
            queryWrapper.eq(Media::getCompanyId, companyId);
        }
        //判断文件名查询条件
        if (StringUtil.isNotBlank(model.getFilename())) {
            queryWrapper.like(Media::getFilename, model.getFilename());
        }
        //判断文件类型查询参数
        if (StringUtil.isNotBlank(model.getType())) {
            queryWrapper.eq(Media::getType, model.getType());
        }

        //对审核状态的查询
        if (StringUtil.isNotBlank(model.getAuditStatus())) {
            queryWrapper.eq(Media::getAuditStatus, model.getAuditStatus());
        }

        //查询结果
        Page<Media> pageResult = this.page(page, queryWrapper);
        List<Media> list = pageResult.getRecords();
        //创建返回对象
        PageVO<Media> pageVO = new PageVO<>();
        pageVO.setPage(params.getPageNo().intValue());
        pageVO.setPageSize(params.getPageSize());
        pageVO.setItems(list);
        pageVO.setCounts(pageResult.getTotal());
        return pageVO;
    }

    /**
     * 预览媒资信息
     *
     * @param mediaId   媒资id
     * @param companyId 机构id
     * @return 播放地址
     * 1.判断关键数据(是否开启事务)
     * 2.判断媒资是否存在
     * 3.判断当前媒资是否删除
     * 4.判断是否是一家机构
     * 5.查询结果并返回
     */
    @Override
    public Object previewMedia(Long mediaId, Long companyId) {
        //1.判断关键数据
        if (ObjectUtils.isEmpty(mediaId) || ObjectUtils.isEmpty(companyId)) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }
        //查询当前媒资
        Media media = this.getById(mediaId);
        //2.判断媒资是否存在
        if (ObjectUtils.isEmpty(media)) {
            ExceptionCast.cast(MediaErrorCode.E_140005);
        }
        //3.判断当前媒资是否删除
        if (!(CommonEnum.USING_FLAG.getCode().equals(media.getStatus()))) {
            ExceptionCast.cast(MediaErrorCode.E_140008);
        }
        //4.判断是否是一家机构
        if (!ObjectUtils.nullSafeEquals(media.getCompanyId(), companyId)) {
            //不是一家机构
            ExceptionCast.cast(CommonErrorCode.E_100108);
        }
        //查询播放地址并返回
        return media.getUrl();
    }

    /**
     * 删除媒资信息
     *
     * @param mediaId   媒资id
     * @param companyId 机构id
     *                  1.判断关键数据
     *                  2.
     */
    @Override
    public void deleteMedia(Long mediaId, Long companyId) {
        //1.判断关键数据
        if (ObjectUtils.isEmpty(mediaId) || ObjectUtils.isEmpty(companyId)) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }
        Media media = this.getById(mediaId);
        //判断是否是一家机构
        if (!media.getCompanyId().equals(companyId)) {
            ExceptionCast.cast(CommonErrorCode.E_100108);
        }

        //2.删除媒资(逻辑删除==>即修改状态(status))
        media.setStatus(CommonEnum.DELETE_FLAG.getCode());
        boolean result = this.updateById(media);
        if (!result) {
            ExceptionCast.cast(MediaErrorCode.E_140002);
        }
    }

    /**
     * 根据id查询媒资信息
     *
     * @param mediaId 媒资id
     * @return 查询结果
     * ======业务分析=========
     * 是否开启事务
     * 不开启
     * 1.判断关键数据
     * 2.判断媒资信息
     * 媒资信息是否存在
     * 媒资信息是否删除
     * 媒资信息是否审核通过
     * 3.使用固定的返回对象封装数据
     * 不管成功失败都需要返回固定返回对象
     */
    @Override
    public RestResponse<MediaDTO> getMediaById4s(Long mediaId) {

        //1.判断关键数据
        if (ObjectUtils.isEmpty(mediaId)) {
            return RestResponse.validfail(CommonErrorCode.E_100101);
        }
        //2.判断媒资信息
        Media media = this.getById(mediaId);
        //2.1媒资信息是否存在
        if (ObjectUtils.isEmpty(media)) {
            return RestResponse.validfail(MediaErrorCode.E_140005);
        }
        //2.2媒资信息是否删除
        if (!(CommonEnum.USING_FLAG.getCode().equals(media.getStatus()))) {
            //不是使用态
            return RestResponse.validfail(MediaErrorCode.E_140008);
        }
        //2.3媒资信息是否审核通过
        if (!(AuditEnum.AUDIT_PASTED_STATUS.getCode().equals(media.getAuditStatus()))) {
            //审核状态不是通过状态
            return RestResponse.validfail(MediaErrorCode.E_140009);
        }
        //3.使用固定的返回对象封装数据
        //将po转为dto
        MediaDTO mediaDTO = MediaConvert.INSTANCE.entity2dto(media);
        //封装结果
        //返回结果
        return RestResponse.success(mediaDTO);
    }

    /**
     * 媒资审核-运营
     *
     * @param media 媒资信息
     *              ==============
     *              业务分析
     *              ==============
     *              1.判断关键数据
     *              2.设置修改条件
     *              3. 根据mp执行修改
     */
    @Override
    public void approve(Media media) {
        //1.判断关键数据
        if (ObjectUtils.isEmpty(media.getId())) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }

        //判断当前媒资是否被删除
        Media queryMedia = this.getById(media.getId());
        if (CommonEnum.DELETE_FLAG.getCode().equals(queryMedia.getStatus())) {
            ExceptionCast.cast(MediaErrorCode.E_140008);
        }

        //2.创建更新内容
        LambdaUpdateWrapper<Media> updateWrapper = new LambdaUpdateWrapper<>();
        //添加更新参数
        if (StringUtil.isNotBlank(media.getAuditMind())) {
            updateWrapper.set(Media::getAuditMind, media.getAuditMind());
        }
        //设置审核状态
        updateWrapper.set(Media::getAuditStatus, media.getAuditStatus());
        //设置更新时间
        updateWrapper.set(Media::getChangeDate, LocalDateTime.now());
        //设置更新条件
        updateWrapper.eq(Media::getId, media.getId());
        //更新审核
        boolean result = this.update(updateWrapper);
        if (!result) {
            ExceptionCast.cast(MediaErrorCode.E_140016);
        }
    }

    /**
     * 媒资预览-运营平台
     *
     * @param mediaId 媒资信息
     *                <p>
     *                ======业务分析=======
     *                1.判断关键数据
     *                mediaId
     *                2.判断业务数据
     *                媒资是否存在
     *                媒资是否被删除
     *                预览播放地址是否存在
     *                3.获取预览地址并且返回
     */
    @Override
    public Object preview(Long mediaId) {

        //1.判断业务数据
        if (ObjectUtils.isEmpty(mediaId)) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }
        //2判断业务数据
        Media media = this.getById(mediaId);
        //媒资是否存在
        if (ObjectUtils.isEmpty(media)) {
            ExceptionCast.cast(MediaErrorCode.E_140005);
        }
        //媒资是否被删除
        if (ObjectUtils.nullSafeEquals(CommonEnum.DELETE_FLAG.getCode(), media.getStatus())) {
            ExceptionCast.cast(MediaErrorCode.E_140008);
        }
        //预览播放地址是否存在
        String url = media.getUrl();
        if (ObjectUtils.isEmpty(url)) {
            ExceptionCast.cast(MediaErrorCode.E_140013);
        }


        return url;
    }

    /**
     * 获取视频播放地址
     *
     * @param mediaId
     * @return
     */
    @Override
    public RestResponse<ResourceUrlModel> getMediaUrl(Long mediaId) {

        // 0.构建资源嫩对象
        ResourceUrlModel resourceUrlModel = new ResourceUrlModel();


        // 1.判断关键数据
        if (ObjectUtils.isEmpty(mediaId)) {
            return RestResponse.validfail(CommonErrorCode.E_100101);
        }

        // 2.判断业务数据
        Media media = this.getById(mediaId);

        if (ObjectUtils.isEmpty(media)) {
            return RestResponse.validfail(MediaErrorCode.E_140005);
        }

        // 3.获得媒资对应的资源路径地址
        // 媒资类型：视频、文档、作业。。。。

        // 判断媒资的类型
        String type = media.getType();
        if (ResourceTypeEnum.VIDEO.getCode().equals(type)) {
            // 如果类型为视频
            // 获得视频的播放地址

            /*
             * 业务优化：
             *   1.获得视频播放地址将保存redis中，并设置有效期
             *   2.如果要进行视频播放
             *       先从redis中获得
             *       如果redis没有数据，从aliyun视频点播中获得播放地址
             * */

            // 由于课上没有上redis，所以为了保证视频播放地址可定能使用，获得播放地址直接从aliyun获得

            // 通过工具类来获得视频播放地址
            try {
                DefaultAcsClient client = AliyunVODUtil.initVodClient(region, accessKeyId, accessKeySecret);


                GetPlayInfoResponse playInfo = AliyunVODUtil.getPlayInfo(client, media.getFileId());

                List<GetPlayInfoResponse.PlayInfo> playInfoList = playInfo.getPlayInfoList();

                if (CollectionUtils.isEmpty(playInfoList)) {
                    ExceptionCast.cast(MediaErrorCode.E_140012);
                } else {
                    // 如果媒资文件视频地址有多个，只会保存一个就可以
                    GetPlayInfoResponse.PlayInfo playInfo1 = playInfoList.get(0);

                    String playURL = playInfo1.getPlayURL();

                    resourceUrlModel.setPlayUrl(playURL);
                }

            } catch (Exception e) {

                log.error("获得视频播放地址失败：{}",e.getMessage());
                return RestResponse.validfail(MediaErrorCode.E_140012);
            }




        } else if (ResourceTypeEnum.DOCUMENT.getCode().equals(type)) {
            //如果是文档
            // 获得文档的访问连接地址

        }

        // 4.返回资源数据----> ResourceUrlModel
        return RestResponse.success(resourceUrlModel);
    }
}
